#include <iostream>
#include <cassert>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;

#define MAX 1024

// 管道没有数据，读端进行等待
int test1()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    cout << "pipefd[0]: " << pipefd[0] << " "
         << "pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork fail\n");
        return 1;
    }

    // 3. 父子进程关闭通道，形成管道，其中子进程负责写，父进程负责读
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 向管道中输入信息
        int count = 0;
        while (true)
        {
            char message[MAX];
            // 向数组中写入信息
            snprintf(message, sizeof(message), "i am child, my pid: %d, count: %d", getpid(), count);
            write(pipefd[1], message, strlen(message));
            count++;
            sleep(1);
        }
        exit(0);
    }
    else
    {
        // 父进程
        // 父进程负责从管道中读取信息，打印出来
        close(pipefd[1]);

        // 从管道中读取信息
        char buffer[MAX];
        while (true)
        {
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                cout << "father pid:" << getpid() << " child say:" << buffer << endl;
            }
        }
    }
}

// 管道被写满了，写端必须等待，直到有空间为止
int test2()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    cout << "pipefd[0]: " << pipefd[0] << " "
         << "pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork fail\n");
        return 1;
    }

    // 3. 父子进程关闭通道，形成管道，其中子进程负责写，父进程负责读
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 向管道中输入信息
        int count = 0;
        while (true)
        {
            char message[MAX];
            // 向数组中写入信息
            snprintf(message, sizeof(message), "i am child, my pid: %d, count: %d", getpid(), count);
            write(pipefd[1], message, strlen(message));
            count++;
        }
        exit(0);
    }
    else
    {
        // 父进程
        // 父进程负责从管道中读取信息，打印出来
        close(pipefd[1]);

        // 从管道中读取信息
        char buffer[MAX];
        while (true)
        {
            sleep(5);
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                cout << "father pid:" << getpid() << " child say:" << buffer << endl;
            }
        }
    }
}

// 写端关闭，读端一直读取，读端读到返回值为0
int test3()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    cout << "pipefd[0]: " << pipefd[0] << " "
         << "pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork fail\n");
        return 1;
    }

    // 3. 父子进程关闭通道，形成管道，其中子进程负责写，父进程负责读
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 向管道中输入信息
        int count = 0;
        while (count < 5)
        {
            char message[MAX];
            // 向数组中写入信息
            snprintf(message, sizeof(message), "i am child, my pid: %d, count: %d", getpid(), count);
            write(pipefd[1], message, strlen(message));
            count++;
        }
        cout << "child quit" << endl;
        // close(pipefd[1]);
        exit(0);
    }
    else
    {
        // 父进程
        // 父进程负责从管道中读取信息，打印出来
        close(pipefd[1]);

        // 从管道中读取信息
        char buffer[MAX];
        while (true)
        {
            sleep(5);
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                cout << "father pid:" << getpid() << " child say:" << buffer << endl;
            }
            else if (n == 0)
            {
                cout << "child quit, father quit" << endl;
                break;
            }
        }
    }

    pid_t rid = waitpid(id, nullptr, 0);
    if (rid == id)
        cout << "wait success" << endl;
    return 0;
}

// 读端关闭，写端一直写入，写端会被操作系统终止
int test4()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);
    cout << "pipefd[0]: " << pipefd[0] << " "
         << "pipefd[1]: " << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork fail\n");
        return 1;
    }

    // 3. 父子进程关闭通道，形成管道，其中子进程负责写，父进程负责读
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 向管道中输入信息
        int count = 0;
        while (true)
        {
            char message[MAX];
            // 向数组中写入信息
            snprintf(message, sizeof(message), "i am child, my pid: %d, count: %d", getpid(), count);
            write(pipefd[1], message, strlen(message));
            count++;
        }
        cout << "child quit" << endl;
        // close(pipefd[1]);
        exit(0);
    }
    else
    {
        // 父进程
        // 父进程负责从管道中读取信息，打印出来
        close(pipefd[1]);

        // 从管道中读取信息
        char buffer[MAX];
        int cnt = 3;
        while (cnt--)
        {
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                cout << "father pid:" << getpid() << " child say:" << buffer << endl;
            }
            else if (n == 0)
            {
                cout << "child quit, father quit" << endl;
                break;
            }
            sleep(1);
        }
        close(pipefd[0]);
        sleep(5);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid == id)
        cout << "wait success" << "exit code is:" << (status & 0x7F) << endl;
    return 0;
}

int main()
{
    int status = test4();
    return status;
}